use std::cell::{Cell, RefCell};
use std::ops::Deref;
use std::rc::Rc;

use objc2::rc::Id;
use objc2::DeclaredClass;
use objc2_ui_kit::{UITouch, UIView};

use super::Window;

#[derive(Clone, Copy)]
pub struct WinRef<'a>(pub &'a XLoopWindow, pub &'a XLoopView);
impl<'a> Deref for WinRef<'a> {
	type Target = XLoopWindow;
	fn deref(&self) -> &Self::Target {
		self.0
	}
}
impl<'a> AsRef<UIView> for WinRef<'a> {
	fn as_ref(&self) -> &UIView {
		self.1
	}
}
impl<'a> types::WinRef<WinRaw> for WinRef<'a> {
	fn raw(&self) -> Option<WinRaw> {
		use core::ffi::c_void;
		let view: &UIView = self.1.as_ref();
		let view = view as *const UIView;
		let view = view as *const c_void;
		let view = view as *mut c_void;
		Some(WinRaw::from(view))
	}
	fn show(&self) {
		let window = self.0;
		window.makeKeyAndVisible();
	}
	fn close(&self) {
		self.0.ivars().view.borrow_mut().take();
		self.0.ivars().controller.borrow_mut().take();
		self.0.ivars().window.borrow_mut().take();
	}
	fn fresh(&self, _reason: &str) {
		self.0.ivars().refresh.set(true);
		// println!("fresh begin {_reason}");
		// self.1.setNeedsDisplay();
		// println!("fresh end");
	}
	fn size(&self) -> (u32, u32) {
		let factor: Factor = self.1.into();
		let frame = self.1.frame();
		(factor.to_u32(frame.size.width), factor.to_u32(frame.size.height))
	}
	fn density(&self) -> f32 {
		self.1.contentScaleFactor() as _
	}
}
#[derive(Debug)]
pub struct WinHandle(pub Id<XLoopWindow>);
impl types::WinHandle<Window> for WinHandle {
	fn with(&self, fun: impl FnOnce(WinRef<'_>)) {
		if let Some(view) = self.0.ivars().view.borrow().clone() {
			fun(WinRef(&self.0, &view));
		}
	}
}

pub struct Vars {
	rep: Rc<dyn types::WinRep<Window>>,
	refresh: Cell<bool>,
	window: RefCell<Option<Id<XLoopWindow>>>,
	controller: RefCell<Option<Id<XLoopController>>>,
	view: RefCell<Option<Id<XLoopView>>>,
	touches: RefCell<Vec<Option<*const UITouch>>>,
}

mod event;
pub use event::*;

mod win;
pub use win::*;

mod view;
pub use view::*;

mod raw;
pub use raw::*;

mod factor;
pub use factor::*;
